<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Application-Level Protocols</title><!--  #exec cmd="/usr/local/bin/meta.pl" -->
  <link href="../Styles/stylesheet.css" rel="stylesheet" type="text/css" />
  <script src="../toc.js" type="text/javascript">



  //<![CDATA[

   <!-- empty -->
  //]]>
  </script>
  <script type="text/javascript">




  /* <![CDATA[ */
    (function() {
        var s = document.createElement("script"), t = document.getElementsByTagName("script")[0];
        s.type = "text/javascript";
        s.async = true;
        s.src = "http://api.flattr.com/js/0.6/load.js?mode=auto";
        t.parentNode.insertBefore(s, t);
    })();
  /* ]]> */
  </script>
  <style type="text/css">
/*<![CDATA[*/

  body { counter-reset: chapter 5; }

  a.sgc-10 {display:none;}
  span.sgc-9 {color: black}
  span.sgc-8 {color: darkmagenta}
  span.sgc-7 {color: green}
  span.sgc-6 {color: blue}
  span.sgc-5 {color: DarkRed}
  span.sgc-4 {color: purple}
  span.sgc-3 {color: firebrick}
  li.sgc-2 {list-style: none}
  div.sgc-1 {text-align: center}
  /*]]>*/
  </style>
</head>

<body>
  <div class="chapter">
    <h1 id="heading_id_2" class="en">Application-Level Protocols</h1>
    <h1 id="heading_id_2" class="zh">应用层协议</h1>
  </div>

  <div class="preface">
    <p class="en">A client and a server exchange messages consisting of message types and message data. This requires design of a suitable message exchange protocol. This chapter looks at some of the issues involved in this, and gives a complete example of a simple client-server application.</p>
    <p class="zh">客户端和服务器的交互包括消息类型和消息数据，这就需要有适当的交互协议。本章着重讨论客户端和服务器交互相关的问题，并给出一个完整又简单的客户端服务器交互的例子。</p>
  </div>

  <div class="generate_from_h2" id="generated-toc"></div>

  <h2 id="heading_id_3" class="en">Introduction</h2>
  <h2 id="heading_id_3" class="zh">介绍</h2>

  <p class="en">A client and server need to exchange information via messages. TCP and UDP provide the transport mechanisms to do this. The two processes also need to have a protocol in place so that message exchange can take place meaningfully. A protocol defines what type of conversation can take place between two components of a distributed application, by specifying messages, data types, encoding formats and so on.</p>
  <p class="zh">客户端和服务器需要通过消息来进行交互。TCP和UDP是信息交互的两种传输机制。在这两种传输机制之上就需要有协议来约定传输内容的含义。协议清楚说明分布式应用的两个模块之间交互消息的消息体、消息的数据类型、编码格式等。
</p>

  <h2 id="heading_id_4" class="en">Protocol Design</h2>
  <h2 id="heading_id_4" class="zh">协议设计</h2>

  <p class="en">There are many possibilities and issues to be decided on when designing a protocol. Some of the issues include:</p>
  <p class="zh">当设计协议的时候，有许多许多的情况和问题需要考虑，比如：
</p>

  <ul class="en">
    <li>Is it to be broadcast or point to point?<br />
    Broadcast must be UDP, local multicast or the more experimental MBONE. Point to point could be either TCP or UDP.</li>

    <li>Is it to be stateful vs stateless?<br />
    Is it reasonable for one side to maintain state about the other side? It is often simpler to do so, but what happens if something crashes?</li>

    <li>Is the transport protocol reliable or unreliable?<br />
    Reliable is often slower, but then you don't have to worry so much about lost messages.</li>

    <li>Are replies needed?<br />
    If a reply is needed, how do you handle a lost reply? Timeouts may be used.</li>

    <li>What data format do you want?<br />
    Two common possibilities are MIME or byte encoding.</li>

    <li>Is your communication bursty or steady stream?<br />
    Ethernet and the Internet are best at bursty traffic. Steady stream is needed for video streams and particularly for voice. If required, how do you manage Quality of Service (QoS)?</li>

    <li>Are there multiple streams with synchronisation required?<br />
    Does the data need to be synchronised with anything? e.g. video and voice.</li>

    <li>Are you building a standalone application or a library to be used by others?<br />
    The standards of documentation required might vary.</li>
  </ul>
  <ul class="zh">
    <li>是广播还是单播？<br />
    广播必须使用UDP，本地组播或者是更成熟的组播骨干网（MBONE）。单播就可以使用TCP或者UDP。
</li>

    <li>消息应该是有状态还是无状态的？<br />
    任意一边是否有必要维持另外一边的状态消息？虽然这个似乎很好实现，但是如果发生异常的时候又应该怎么做呢？
</li>

    <li>协议是可靠服务还是不可靠服务？<br />
    一般来说，提供可靠服务就会更慢些，但好处是不需要考虑丢失消息的情况。</li>

    <li>是否需要响应？<br />
    如果是需要响应的话，如何处理没有响应回复的情况？或许可以设置超时时间。
</li>

    <li>你想要使用什么数据格式？<br />
    一般使用两种格式：MIME和字节流</li>

    <li>消息流是使用突发性的还是稳定性的？<br />
    Ethernet和Internet最好使用突发性消息流。稳定性的消息流通常是用在视频和声频流传输上。如果要求的话，你如何保证你的服务质量（QoS）？</li>

    <li>有多流同步的需求吗？<br />
    是否有多种数据流需要进行同步？比如视频流和声频流。</li>

    <li>建立的是单独的应用还是需要给别人使用的库？<br />
    可能需要花很大的精力编写标准文档。
</li>
  </ul>

  <h2 id="heading_id_5" class="en">Version control</h2>
  <h2 id="heading_id_5" class="zh">版本控制</h2>

  <p class="en">A protocol used in a client/server system will evolve over time, changing as the system expands. This raises compatability problems: a version 2 client will make requests that a version 1 server doesn't understand, whereas a version 2 server will send replies that a version 1 client won't understand.</p>
  <p class="zh">随着时间变化和系统的升级，客户端/服务器之间的协议也会升级。这可能会引起兼容性的问题：版本2的客户端发出的请求可能版本1的服务器无法解析，反之也一样，版本2的服务器回复的消息版本1的客户端无法解析。
</p>

  <p class="en">Each side should ideally be able to understand messages for its own version and all earlier ones. It should be able to write replies to old style queries in old style response format.</p>
  <p class="zh">理想情况下，不论是哪一端，都应该既能满足自己当前版本的消息规范，也能满足早期版本的消息规范。任意一端对于旧版本的请求应该返回旧版本的响应。</p>

  <div class="sgc-1"><img alt="" src="../Images/version.png" /></div>

  <p class="en">The ability to talk earlier version formats may be lost if the protocol changes too much. In this case, you need to be able to ensure that no copies of the earlier version still exist - and that is generally imposible.</p>
  <p class="zh">但是如果协议变化太大的话，可能就很难保持与早期版本的兼容了。在这种情况下，你就需要保证已经不存在早期的版本了 -- 当然这个几乎是不可能的。
</p>

  <p class="en">Part of the protocol setup should involve version information.</p>
  <p class="zh">所以，协议应该包含有版本消息。</p>

  <h3 id="heading_id_6" class="en">The Web</h3>
  <h3 id="heading_id_6" class="ch">Web协议</h3>

  <p class="en">The Web is a good example of a system that is messed up by different versions. The protocol has been through three versions, and most servers/browsers now use the latest version. The version is given in each request</p>
  <p class="zh">Web协议就是一个由于有不同协议版本同时存在而出现混乱的例子。Web协议已经有三个版本了，通常服务器和浏览器都是使用最新的版本，版本消息包含在请求中：</p>

  <div class="sgc-1">
    <table border="1">
      <tr>
        <th>request</th>

        <th>version</th>
      </tr>

      <tr>
        <td><code>GET /</code></td>

        <td>pre 1.0</td>
      </tr>

      <tr>
        <td><code>GET / HTTP/1.0</code></td>

        <td>HTTP 1.0</td>
      </tr>

      <tr>
        <td><code>GET / HTTP/1.1</code></td>

        <td>HTTP 1.1</td>
      </tr>
    </table>
  </div>

  <p class="en">But the <em>content</em> of the messages has been through a large number of versions:</p>
  <p class="zh">但是<em>消息体的内容</em>已经被大量版本制定修改过：</p>

  <ul class="en">
    <li>HTML versions 1-4 (all different), with version 5 on the horizon;</li>

    <li>non-standard tags recognised by different browsers;</li>

    <li>non-HTML documents often require content handlers that may or may not be present - does your browser have a handler for Flash?</li>

    <li>inconsistent treatment of document content (e.g. some stylesheet content will crash some browsers)</li>

    <li>Different support for JavaScript (and different versions of JavaScript)</li>

    <li>Different runtime engines for Java</li>

    <li>Many pages do not conform to <em>any</em> HTML versions (e.g. with syntax errors)</li>
  </ul>
  <ul class="zh">
    <li>HTML版本1-4（每个版本都不一样），还有即将到来的HTML5;</li>

    <li>不同浏览器各自支持非标准化的标签;</li>

    <li>HTML文档之外的内容也通常需要不同的内容处理器 -- 比如你的浏览器支持Flash播放器吗？</li>

    <li>文档内容的不一致的处理方法（例如，在一些浏览器上，有的css会冲突）</li>

    <li>浏览器对JavaScript的不同支持程度（当然JavaScript也同时存在不同的版本）</li>

    <li>不同的Java运行引擎</li>

    <li>有的页面并没有遵守<em>任何</em>HTML版本规范（比如HTML格式错误的页面）</li>
  </ul>

  <h2 id="heading_id_7" class="en">Message Format</h2>
  <h2 id="heading_id_7" class="zh">消息格式</h2>

  <p class="en">In the last chapter we discussed some possibilities for representing data to be sent across the wire. Now we look one level up, to the messages which may contain such data.</p>
  <p class="zh">上一章我们讨论了数据传输的几种可能的表现形式。现在我们进一步研究包含数据的消息。</p>

  <ul class="en">
    <li>The client and server will exchange messages with different meanings. e.g.

      <ul>
        <li>Login request,</li>

        <li>get record request,</li>

        <li>login reply,</li>

        <li>record data reply.</li>
      </ul>
    </li>

    <li>The client will prepare a request which must be understood by the server.</li>

    <li>The server will prepare a reply which must be understood by the client.</li>
  </ul>

  <ul class="zh">
    <li>客户端和服务器会交换不同含义的消息，比如：

      <ul>
        <li>登陆请求</li>

        <li>获取某些记录的请求</li>

        <li>登陆请求的回复</li>

        <li>获取某些记录请求的回复</li>
      </ul>
    </li>

    <li>客户端必须发送能被服务器解析的请求。</li>

    <li>服务器必须回复能被客户端解析的响应。</li>
  </ul>

  <p class="en">Commonly, the first part of the message will be a message type.</p>
  <p class="zh">通常来说，消息的头部必须包含消息类型。</p>

  <ul class="en">
    <li>Client to server
      <pre><code>
      LOGIN name passwd
      GET cpe4001 grade
      </code></pre>
    </li>

    <li>Server to client
      <pre><code>
      LOGIN succeeded
      GRADE cpe4001 D
      </code></pre>
    </li>
  </ul>

  <ul class="zh">
    <li>客户端发送给服务器
      <pre><code>
      LOGIN name passwd
      GET cpe4001 grade
      </code></pre>
    </li>

    <li>服务器返回给客户端
      <pre><code>
      LOGIN succeeded
      GRADE cpe4001 D
      </code></pre>
    </li>
  </ul>

  <p class="en">The message types can be strings or integers. e.g. HTTP uses integers such as 404 to mean "not found" (although these integers are written as strings). The messages from client to server and vice versa are disjoint: "LOGIN" from client to server is different to "LOGIN" from server to client.</p>
  <p class="zh">消息类型应该设置为字符型或者整型。比如，HTTP使用整数404来表示“未找到资源”（尽管这个整型是被当做字符串使用）。客户端到服务器的消息和服务器到客户端的消息是不一样的：比如从客户端到服务器的“LOGIN”消息就不同于服务器到客户端的“LOGIN”消息。</p>

  <h2 id="heading_id_8" class="en">Data Format</h2>
  <h2 id="heading_id_8" class="zh">数据格式</h2>

  <p class="en">There are two main format choices for messages: byte encoded or character encoded.</p>
  <p class="zh">对于消息来说，有两种主要的数据格式可供选择：字节编码和字符编码。</p>

  <h3 id="heading_id_9" class="en">Byte format</h3>
  <h3 id="heading_id_9" class="zh">字节编码</h3>
  <p class="en">In the byte format</p>
  <p class="zh">对于字节编码</p>

  <ul class="en">
    <li>the first part of the message is typically a byte to distinguish between message types.</li>

    <li>The message handler would examine this first byte to distinguish message type and then perform a switch to select the appropriate handler for that type.</li>

    <li>Further bytes in the message would contain message content according to a pre-defined format (as discussed in the previous chapter).</li>
  </ul>

  <ul class="zh">
    <li>消息的头部通常使用一个字节来标示消息的类型。</li>

    <li>消息处理者应该根据消息头部的类型字节来选择合适的处理程序来处理这个类型的消息。</li>

    <li>消息后面的字节应该是根据事先定义的格式（上一章节讨论的）来包含消息具体内容。</li>
  </ul>

  <p class="en">The advantages are compactness and hence speed. The disadvantages are caused by the opaqueness of the data: it may be harder to spot errors, harder to debug, require special purpose decoding functions. There are many examples of byte-encoded formats, including major protocols such as DNS and NFS , upto recent ones such as Skype. Of course, if your protocol is not publicly specified, then a byte format can also make it harder for others to reverse-engineer it!</p>
  <p class="zh">字节编码的优势就是紧凑小巧，传输速度快。劣势就是数据的不透明性：字节编码很难定位错误，也很难调试。往往是要求写一些额外的解码函数。有许多字节编码格式的例子，大部分协议都是使用字节编码，例如DNS和NFS协议，还有最近出现的Skype协议。当然，如果你的协议没有公开说明结构，使用字节编码可以让其他人使用反向工程手段很难破解!</p>

  <p class="en">Pseudocode for a byte-format server is</p>
  <p class="zh">字节编码的服务器的伪代码如下</p>
  <pre><code>
    handleClient(conn) {
        while (true) {
            byte b = conn.readByte()
            switch (b) {
                case MSG_1: ...
                case MSG_2: ...
                ...
            }
        }
    }
</code></pre>

  <p class="en">Go has basic support for managing byte streams. The interface <code>Conn</code> has methods</p>
  <p class="zh">Go提供了基本的管理字节流的方法。 接口<code>Conn</code> 包含有方法</p>
  <pre><code>
(c Conn) Read(b []byte) (n int, err os.Error)
(c Conn) Write(b []byte) (n int, err os.Error)
    </code></pre>

  <p class="en">and these methods are implemented by <code>TCPConn</code> and <code>UDPConn</code>.</p>
  <p class="zh">这两个方法的具体实现类有 <code>TCPConn</code> and <code>UDPConn</code>。</p>

  <h3 id="heading_id_10" class="en">Character Format</h3>
  <h3 id="heading_id_10" class="zh">字符编码</h3>

  <p class="en">In this mode, everything is sent as characters if possible. For example, an integer 234 would be sent as, say, the three characters '2', '3' and '4' instead of the one byte 234. Data that is inherently binary may be base64 encoded to change it into a 7-bit format and then sent as ASCII characters, as discussed in the previous chapter.</p>
  <p class="zh">在这个编码模式下，所有消息都尽可能以字符的形式发送。例如，整型数字234会被处理成三个字符‘2’，‘3’，‘4’，而不会被处理成234的字节码。二进制数据将会使用base64编码变成为7-bit的格式，然后当做ASCII码传递，就和我们上一章讨论的一样。</p>

  <p class="en">In character format,</p>
  <p class="zh">对于字符编码,</p>

  <ul class="en">
    <li>A message is a sequence of one or more lines</li>

    <li class="sgc-2">The start of the first line of the message is typically a word that represents the message type.</li>

    <li>String handling functions may be used to decode the message type and data.</li>

    <li>The rest of the first line and successive lines contain the data.</li>

    <li>Line-oriented functions and line-oriented conventions are used to manage this.</li>
  </ul>

  <ul class="zh">
    <li>一条消息会是一行或者很多行内容</li>

    <li class="sgc-2">消息的第一行通常使用一个单词来说明消息的类型。</li>

    <li>使用字符处理函数来解码消息类型和消息内容。</li>

    <li>第一行之后的信息和其他行包含消息数据。</li>

    <li>使用行处理函数和行处理规范来处理消息。</li>
  </ul>

  <p class="en">Pseudocode is</p>
  <p class="zh">伪代码如下</p>
  <pre><code>
handleClient() {
    line = conn.readLine()
    if (line.startsWith(...) {
        ...
    } else if (line.startsWith(...) {
        ...
    }
}
</code></pre>

  <p class="en">Character formats are easier to setup and easier to debug. For example, you can use <code>telnet</code> to connect to a server on any port, and send client requests to that server. It isn't so easy the other way, but you can use tools like <code>tcpdump</code> to snoop on TCP traffic and see immediately what clients are sending to servers.</p>
  <p class="zh">字符编码很容易进行组装，也很容易调试。例如，你可以<code>telnet</code>连接到一台服务器的端口上，然后发送客户的请求到服务器。其他的编码方式无法轻易地监听请求。但是对于字符编码，你可以使用<code>tcpdump</code> 这样的工具监听TCP的交互，并且立刻就能看到客户端发送给服务器端的消息。</p>

  <p class="en">There is not the same level of support in Go for managing character streams. There are significant issues with character sets and character encodings, and we will explore these issues in a later chapter.</p>
  <p class="zh">在Go中没有像字节流那样专门处理字符流的工具。如何处理字符集和字符编码是非常重要的，我们将会在下一章专门讨论这些问题。</p>

  <p class="en">If we just pretend everything is ASCII, like it was once upon a time, then character formats are quite straightforward to deal with. The principal complication at this level is the varying status of "newline" across different operating systems. Unix uses the single character '\n'. Windows and others (more correctly) use the pair "\r\n". On the internet, the pair "\r\n" is most common - Unix systems just need to take care that they don't assume '\n'.</p>
  <p class="zh">如果和以前一样，处理的所有字符都是ASCII码，那么我们能直接又简单地处理这些字符。但是实际上，字符处理复杂的原因是不同的操作系统上有各种不统一的“换行符”。Unix使用简单的'\n' 来表示换行，Windows和其他的系统（这种方法更正确）使用“\r\n”来表示。在实际的网络传输中，使用一对“\r\n”是更通用的方案 -- 因为Unix系统只需要注意不要设定换行符只有“\n”就可以满足这个方案。</p>

  <h2 id="heading_id_11" class="en">Simple Example</h2>
  <h2 id="heading_id_11" class="zh">简单的例子</h2>

  <p class="en">This example deals with a directory browsing protocol - basically a stripped down version of FTP, but without even the file transfer part. We only consider listing a directory name, listing the contents of a directory and changing the current directory - all on the server side, of course. This is a complete worked example of creating all components of a client-server application. It is a simple program which includes messages in both directions, as well as design of messaging protocol.</p>
  <p class="zh">这个例子展示的是一个文件夹浏览协议 -- 基本上就是一个简单的FTP协议，只是连FTP的文件传输都没有实现。我们考虑这个例子包含的功能有：展示文件夹名称，列出文件夹内包含的文件，改变当前文件夹路径 -- 当然所有这些文件都是在服务器的。这是一个完整的包含客户端和服务器的例子。这个简单的程序既需要两个方向的消息交互，也需要消息的具体协议设计。</p>

  <p class="en">Look at a simple non-client-server program that allows you to list files in a directory and change and print the directory on the server. We omit copying files, as that adds to the length of the program without really introducing important concepts. For simplicity, all filenames will be assumed to be in 7-bit ASCII. If we just looked at a standalone application first, then the pseudo-code would be</p>
  <p class="zh">在开始例子之前，我们先看一个简单的程序，这个程序不是客户端和服务器交互的程序，它实现的功能包括：展示文件夹中的文件，打印出文件夹在服务器上的路径。在这里我们忽略正在拷贝中的文件，因为考虑这些细节会增加代码长度，却对我们要介绍的重要概念没有什么帮助。简单假设：所有的文件名都是7位的ASCII码。先考虑这个独立的程序，它的伪代码应该是：</p>
  <pre>
<code>
read line from user
while not eof do
  if line == dir
    list directory
  else

  if line == cd &lt;dir&gt;
    change directory
  else

  if line == pwd
    print directory
  else

  if line == quit
    quit
  else
    complain

  read line from user
</code>
</pre>

  <p class="en">A non-distributed application would just link the UI and file access code</p>
  <p class="zh">一个非分布式的应用是将UI和文件存储代码连接起来</p>

  <div class="sgc-1"><img alt="" src="../Images/single.png" /></div>

  <p class="en">In a client-server situation, the client would be at the user end, talking to a server somewhere else. Aspects of this program belong solely at the presentation end, such as getting the commands from the user. Some are messages from the client to the server, some are solely at the server end.</p>
  <p class="zh">在包含有客户端和服务器的情况下，客户端就代表用户终端，用来和服务器交互。这个程序最独立的部分就是表现层，比如如何获取用户的命令等。这个程序的消息有的是从客户端到服务器，有的只是在服务器。</p>

  <div class="sgc-1"><img alt="" src="../Images/dist.png" /></div>

  <p class="en">For a simple directory browser, assume that all directories and files are at the server end, and we are only transferring file information from the server to the client. The client side (including presentation aspects) will become</p>
  <p class="zh">对于简单的文件夹浏览器来说，假设所有的文件夹和文件都是在服务器端，我们也只需要从服务器传递文件消息给客户端。客户端的伪代码（包括表现层）应该如下：</p>
  <pre>
<code>
read line from user
while not eof do
  if line == dir
    <em>list directory</em>
  else

  if line == cd &lt;dir&gt;
    <em>change directory</em>
  else

  if line == pwd
    <em>print directory</em>
  else

  if line == quit
    quit
  else
    complain

  read line from user
</code>
</pre>

  <p class="en">where the italicised lines involve communication with the server.</p>
  <p class="zh">上面斜体字的部分是代表需要与服务器进行交互的命令。</p>

  <h3 id="heading_id_12" class="en">Alternative presentation aspects</h3>
  <h3 id="heading_id_12" class="zh">改变表现层</h3>

  <p class="en">A GUI program would allow directory contents to be displayed as lists, for files to be selected and actions such as change directory to be be performed on them. The client would be controlled by actions associated with various events that take place in graphical objects. The pseudo-code might look like</p>
  <p class="zh">GUI程序可以很方便展示文件夹内容，选择文件，做一些诸如改变文件夹路径的操作。客户端被图形化对象中的各种定义好的事件所驱动从而实现功能。伪代码如下：</p>
  <pre>
<code>
change dir button:
  if there is a selected file
    <em>change directory</em>
  if successful
    update directory label
    <em>list directory</em>
    update directory list
</code></pre>

  <p class="en">The functions called from the different UI's should be the same - changing the presentation should not change the networking code</p>
  <p class="zh">不同的UI实现的功能都是一样的 -- 改变表现层并不需要改变网络传输的代码</p>

  <h3 id="heading_id_13" class="en">Protocol - informal</h3>
  <h3 id="heading_id_13" class="zh">协议 -- 概述</h3>

  <div class="sgc-1">
    <table border="1">
      <tr>
        <th>client request</th>

        <th>server response</th>
      </tr>

      <tr>
        <td>dir</td>

        <td>send list of files</td>
      </tr>

      <tr>
        <td>cd &lt;dir&gt;</td>

        <td>change dir<br />
        send error if failed<br />
        send ok if succeed</td>
      </tr>

      <tr>
        <td>pwd</td>

        <td>send current directory</td>
      </tr>

      <tr>
        <td>quit</td>

        <td>quit</td>
      </tr>
    </table>
  </div>

  <h3 id="heading_id_14" class="en">Text protocol</h3>
  <h3 id="heading_id_14" class="zh">文本传输协议</h3>

  <p class="en">This is a simple protocol. The most complicated data structure that we need to send is an array of strings for a directory listing. In this case we don't need the heavy duty serialisation techniques of the last chapter. In this case we can use a simple text format.</p>
  <p class="zh">这是一个简单的协议，最复杂的部分就是我们需要使用字符串数组来列出文件夹中内容。所以，我们就不使用最后一章讲到的繁琐复杂的序列化技术了，仅仅使用一种简单的文本格式就好了。</p>

  <p class="en">But even if we make the protocol simple, we still have to specify it in detail. We choose the following message format:</p>
  <p class="zh">但是实际上，即使我们想尽量使得协议简单，在细节上也需要考虑清楚。我们使用下面的消息格式约定：</p>

  <ul class="en">
    <li>All messages are in 7-bit US-ASCII</li>

    <li>The messages are case-sensitive</li>

    <li>Each message consists of a sequence of lines</li>

    <li>The first word on the first line of each message describes the message type. All other words are message data</li>

    <li>All words are separated by exactly one space character</li>

    <li>Each line is terminated by CR-LF</li>
  </ul>

  <ul class="zh">
    <li>所有的消息都是7位的US-ASCII码</li>

    <li>所有的消息都是大小写敏感</li>

    <li>每条消息都是由一系列的行组成</li>

    <li>第一行的第一个单词是用来说明消息类型，其他单词都是具体的消息数据</li>

    <li>相邻的单词应该只有一个空格符分隔</li>

    <li>每一行以CR-LF作为结束符</li>
  </ul>

  <p class="en">Some of the choices made above are weaker in real-life protocols. For example</p>
  <p class="zh">实际上，上面的一些考虑在真实的协议中是远远不够的。比如</p>

  <ul class="en">
    <li>Message types could be case-insensitive. This just requires mapping message type strings down to lower-case before decoding</li>

    <li>An arbitrary amount of white space could be left between words. This just adds a little more complication, compressing white space</li>

    <li>Continuation characters such as '\' can be used to break long lines over several lines. This starts to make processing more complex</li>

    <li>Just a '\n' could be used as line terminator, as well as '\r\n'. This makes recognising end of line a bit harder</li>
  </ul>

  <ul class="zh">
    <li>消息类型类型应该是大小不写敏感的。 对于表示消息类型的字符串，我们就需要在解码前将它小写化</li>

    <li>单词与单词间的多余空白字符应该被丢弃掉。当然这会增加一些代码的复杂度，去处理压缩空白符</li>

    <li>像“\”这样的续行符应该被使用，它能将一个大的长句子分隔成几行。从这里开始，程序渐渐变得更复杂了</li>

    <li>像“\n”这样的字符也应该能被解析为换行符，就和“\r\n”一样。这个就让辨识解析程序的结束符更为复杂了</li>
  </ul>

  <p class="en">All of these variations exist in real protocols. Cumulatively, they make the string processing just more complex than in our case.</p>
  <p class="zh">所有以上的变化和考虑都会在真实使用的协议中出现。渐渐地，这些会导致实际的字符处理程序比我们的这个例子复杂。</p>

  <div class="sgc-1">
    <table border="1">
      <tr>
        <th>client request</th>

        <th>server response</th>
      </tr>

      <tr>
        <td>send "DIR"</td>

        <td>send list of files, one per line<br />
        terminated by a blank line</td>
      </tr>

      <tr>
        <td>send "CD &lt;dir&gt;"</td>

        <td>change dir<br />
        send "ERROR" if failed<br />
        send "OK"</td>
      </tr>

      <tr>
        <td>send "PWD"</td>

        <td>send current working directory</td>
      </tr>
    </table>
  </div>

  <h3 id="heading_id_15" class="en">Server code</h3>
  <h3 id="heading_id_15" class="zh">服务器代码</h3>
  <pre><code><span class="sgc-9">
<span class="sgc-3">/* FTP Server
 */
</span><span class="sgc-4">package</span> main

<span class="sgc-4">import</span> (
        <span class="sgc-5">"fmt"</span>
        <span class="sgc-5">"net"</span>
        <span class="sgc-5">"os"</span>
)

const (
        <span class="sgc-6">DIR</span> = <span class="sgc-5">"DIR"</span>
        <span class="sgc-6">CD</span>  = <span class="sgc-5">"CD"</span>
        <span class="sgc-6">PWD</span> = <span class="sgc-5">"PWD"</span>
)

<span class="sgc-4">func</span> main() {

        service := <span class="sgc-5">"0.0.0.0:1202"</span>
        tcpAddr, err := net.<span class="sgc-6">ResolveTCPAddr</span>(<span class="sgc-5">"tcp"</span>, service)
        checkError(err)

        listener, err := net.<span class="sgc-6">ListenTCP</span>(<span class="sgc-5">"tcp"</span>, tcpAddr)
        checkError(err)

        <span class="sgc-4">for</span> {
                conn, err := listener.<span class="sgc-6">Accept</span>()
                <span class="sgc-4">if</span> err != nil {
                        <span class="sgc-4">continue</span>
                }
                go handleClient(conn)
        }
}

<span class="sgc-4">func</span> handleClient(conn net.<span class="sgc-6">Conn</span>) {
        defer conn.<span class="sgc-6">Close</span>()

        <span class="sgc-4">var</span> buf [512]<span class="sgc-7">byte</span>
        <span class="sgc-4">for</span> {
                n, err := conn.<span class="sgc-6">Read</span>(buf[0:])
                <span class="sgc-4">if</span> err != nil {
                        conn.<span class="sgc-6">Close</span>()
                        <span class="sgc-4">return</span>
                }

                s := string(buf[0:n])
                <span class="sgc-3">// decode request
</span>         <span class="sgc-4">if</span> s[0:2] == <span class="sgc-6">CD</span> {
                        chdir(conn, s[3:])
                } <span class="sgc-4">else</span> <span class="sgc-4">if</span> s[0:3] == <span class="sgc-6">DIR</span> {
                        dirList(conn)
                } <span class="sgc-4">else</span> <span class="sgc-4">if</span> s[0:3] == <span class="sgc-6">PWD</span> {
                        pwd(conn)
                }

        }
}

<span class="sgc-4">func</span> chdir(conn net.<span class="sgc-6">Conn</span>, s string) {
        <span class="sgc-4">if</span> os.<span class="sgc-6">Chdir</span>(s) == nil {
                conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(<span class="sgc-5">"OK"</span>))
        } <span class="sgc-4">else</span> {
                conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(<span class="sgc-5">"ERROR"</span>))
        }
}

<span class="sgc-4">func</span> pwd(conn net.<span class="sgc-6">Conn</span>) {
        s, err := os.<span class="sgc-6">Getwd</span>()
        <span class="sgc-4">if</span> err != nil {
                conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(<span class="sgc-5">""</span>))
                <span class="sgc-4">return</span>
        }
        conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(s))
}

<span class="sgc-4">func</span> dirList(conn net.<span class="sgc-6">Conn</span>) {
        defer conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(<span class="sgc-5">"\r\n"</span>))

        dir, err := os.<span class="sgc-6">Open</span>(<span class="sgc-5">"."</span>)
        <span class="sgc-4">if</span> err != nil {
                <span class="sgc-4">return</span>
        }

        names, err := dir.<span class="sgc-6">Readdirnames</span>(-1)
        <span class="sgc-4">if</span> err != nil {
                <span class="sgc-4">return</span>
        }
        <span class="sgc-4">for</span> <span class="sgc-8">_</span>, nm := range names {
                conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(nm + <span class="sgc-5">"\r\n"</span>))
        }
}

<span class="sgc-4">func</span> checkError(err error) {
        <span class="sgc-4">if</span> err != nil {
                fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"Fatal error "</span>, err.<span class="sgc-6">Error</span>())
                os.<span class="sgc-6">Exit</span>(1)
        }
}
</span></code></pre>

  <h3 id="heading_id_16" class="en">Client code</h3>
  <h3 id="heading_id_16" class="zh">客户端代码</h3>
  <pre><code><span class="sgc-9">
<span class="sgc-3">/* FTPClient
 */
</span><span class="sgc-4">package</span> main

<span class="sgc-4">import</span> (
        <span class="sgc-5">"fmt"</span>
        <span class="sgc-5">"net"</span>
        <span class="sgc-5">"os"</span>
        <span class="sgc-5">"bufio"</span>
        <span class="sgc-5">"strings"</span>
        <span class="sgc-5">"bytes"</span>
)

<span class="sgc-3">// strings used by the user interface
</span>const (
        uiDir  = <span class="sgc-5">"dir"</span>
        uiCd   = <span class="sgc-5">"cd"</span>
        uiPwd  = <span class="sgc-5">"pwd"</span>
        uiQuit = <span class="sgc-5">"quit"</span>
)

<span class="sgc-3">// strings used across the network
</span>const (
        <span class="sgc-6">DIR</span> = <span class="sgc-5">"DIR"</span>
        <span class="sgc-6">CD</span>  = <span class="sgc-5">"CD"</span>
        <span class="sgc-6">PWD</span> = <span class="sgc-5">"PWD"</span>
)

<span class="sgc-4">func</span> main() {
        <span class="sgc-4">if</span> len(os.<span class="sgc-6">Args</span>) != 2 {
                fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"Usage: "</span>, os.<span class="sgc-6">Args</span>[0], <span class="sgc-5">"host"</span>)
                os.<span class="sgc-6">Exit</span>(1)
        }

        host := os.<span class="sgc-6">Args</span>[1]

        conn, err := net.<span class="sgc-6">Dial</span>(<span class="sgc-5">"tcp"</span>, host+<span class="sgc-5">":1202"</span>)
        checkError(err)

        reader := bufio.<span class="sgc-6">NewReader</span>(os.<span class="sgc-6">Stdin</span>)
        <span class="sgc-4">for</span> {
                line, err := reader.<span class="sgc-6">ReadString</span>('\n')
                <span class="sgc-3">// lose trailing whitespace
</span>         line = strings.<span class="sgc-6">TrimRight</span>(line, <span class="sgc-5">" \t\r\n"</span>)
                <span class="sgc-4">if</span> err != nil {
                        <span class="sgc-4">break</span>
                }

                <span class="sgc-3">// split into command + arg
</span>         strs := strings.<span class="sgc-6">SplitN</span>(line, <span class="sgc-5">" "</span>, 2)
                <span class="sgc-3">// decode user request
</span>         <span class="sgc-4">switch</span> strs[0] {
                <span class="sgc-4">case</span> uiDir:
                        dirRequest(conn)
                <span class="sgc-4">case</span> uiCd:
                        <span class="sgc-4">if</span> len(strs) != 2 {
                                fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"cd &lt;dir&gt;"</span>)
                                <span class="sgc-4">continue</span>
                        }
                        fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"CD \""</span>, strs[1], <span class="sgc-5">"\""</span>)
                        cdRequest(conn, strs[1])
                <span class="sgc-4">case</span> uiPwd:
                        pwdRequest(conn)
                <span class="sgc-4">case</span> uiQuit:
                        conn.<span class="sgc-6">Close</span>()
                        os.<span class="sgc-6">Exit</span>(0)
                <span class="sgc-4">default</span>:
                        fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"Unknown command"</span>)
                }
        }
}

<span class="sgc-4">func</span> dirRequest(conn net.<span class="sgc-6">Conn</span>) {
        conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(<span class="sgc-6">DIR</span> + <span class="sgc-5">" "</span>))

        <span class="sgc-4">var</span> buf [512]<span class="sgc-7">byte</span>
        result := bytes.<span class="sgc-6">NewBuffer</span>(nil)
        <span class="sgc-4">for</span> {
                <span class="sgc-3">// read till we hit a blank line
</span>         n, <span class="sgc-8">_</span> := conn.<span class="sgc-6">Read</span>(buf[0:])
                result.<span class="sgc-6">Write</span>(buf[0:n])
                length := result.<span class="sgc-6">Len</span>()
                contents := result.<span class="sgc-6">Bytes</span>()
                <span class="sgc-4">if</span> string(contents[length-4:]) == <span class="sgc-5">"\r\n\r\n"</span> {
                        fmt.<span class="sgc-6">Println</span>(string(contents[0 : length-4]))
                        <span class="sgc-4">return</span>
                }
        }
}

<span class="sgc-4">func</span> cdRequest(conn net.<span class="sgc-6">Conn</span>, dir string) {
        conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(<span class="sgc-6">CD</span> + <span class="sgc-5">" "</span> + dir))
        <span class="sgc-4">var</span> response [512]<span class="sgc-7">byte</span>
        n, <span class="sgc-8">_</span> := conn.<span class="sgc-6">Read</span>(response[0:])
        s := string(response[0:n])
        <span class="sgc-4">if</span> s != <span class="sgc-5">"OK"</span> {
                fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"Failed to change dir"</span>)
        }
}

<span class="sgc-4">func</span> pwdRequest(conn net.<span class="sgc-6">Conn</span>) {
        conn.<span class="sgc-6">Write</span>([]<span class="sgc-7">byte</span>(<span class="sgc-6">PWD</span>))
        <span class="sgc-4">var</span> response [512]<span class="sgc-7">byte</span>
        n, <span class="sgc-8">_</span> := conn.<span class="sgc-6">Read</span>(response[0:])
        s := string(response[0:n])
        fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"Current dir \""</span> + s + <span class="sgc-5">"\""</span>)
}

<span class="sgc-4">func</span> checkError(err error) {
        <span class="sgc-4">if</span> err != nil {
                fmt.<span class="sgc-6">Println</span>(<span class="sgc-5">"Fatal error "</span>, err.<span class="sgc-6">Error</span>())
                os.<span class="sgc-6">Exit</span>(1)
        }
}
</span></code></pre>

  <h2 id="heading_id_17" class="en">State</h2>
  <h2 id="heading_id_17" class="zh">状态</h2>

  <p class="en">Applications often make use of state information to simplify what is going on. For example</p>
  <p class="zh">应用程序经常保存状态消息来简化下面要做的事情，比如</p>

  <ul class="en">
    <li>Keeping file pointers to current file location</li>

    <li>Keeping current mouse position</li>

    <li>Keeping current customer value.</li>
  </ul>
  <ul class="zh">
    <li>保存当前文件路径的文件指针状态</li>

    <li>保存当前的鼠标位置状态</li>

    <li>保存当前的客户值状态</li>
  </ul>

  <p class="en">In a distributed system, such state information may be kept in the client, in the server, or in both.</p>
  <p class="zh">在分布式的系统中，这样的状态消息可能是保存在客户端，服务器，也可能两边都保存。</p>

  <p class="en">The important point is to whether one process is keeping state information about <i>itself</i> or about the <i>other</i> process. One process may keep as much state information about itself as it wants, without causing any problems. If it needs to keep information about the state of the other process, then problems arise: the process' actual knowledge of the state of the other may become incorrect. This can be caused by loss of messages (in UDP), by failure to update, or by s/w errors.</p>
  <p class="zh">最重要的一点是，进程是否需要保存 <i>自身进程</i> 或者<i>其他进程</i> 的状态消息。一个进程保存再多自己的状态信息，也不会引发其他问题。如果需要保存其他进程的状态消息，这个问题就复杂了：当前保存的其他进程的状态消息和实际的状态消息可能是不一致的。这可能会引起消息丢失（在UDP中）、更新失败、或者s/w错误等。</p>

  <p class="en">An example is reading a file. In single process applications the file handling code runs as part of the application. It maintains a table of open files and the location in each of them. Each time a read or write is done this file location is updated. In the DCE file system, the file server keeps track of a client's open files, and where the client's file pointer is. If a message could get lost (but DCE uses TCP) these could get out of synch. If the client crashes, the server must eventually timeout on the client's file tables and remove them.</p>
  <p class="zh">一个例子就是读取文件。在单个进程中，文件处理代码是应用程序的一部分。它维持一个表，表中包含所有打开的文件和文件指针位置。每次文件读写的时候，文件指针位置就会更新。在数据通信（DCE）文件系统中，文件系统必须追踪客户端打开了哪些文件，客户端的文件指针在哪。如果一个消息丢失了（但是DCE是使用TCP的），这些状态消息就不能保持同步了。如果出现客户端崩溃了，服务器就必须对这个表触发超时并删除。</p>

  <div class="sgc-1"><img alt="" src="../Images/dce.png" /></div>

  <p class="en">In NFS, the server does not maintain this state. The client does. Each file access from the client that reaches the server must open the file at the appropriate point, as given by the client, to perform the action.</p>
  <p class="zh">在NFS文件系统中，服务器并没有保存这个状态消息，而是有客户端保存的。客户端每次在服务器进行的读取文件操作必须能在准确的文件位置打开文件，而这个文件位置是由客户端提供的，从而才能进行后续的操作。</p>

  <div class="sgc-1"><img alt="" src="../Images/nfs.png" /></div>

  <p class="en">If the server maintains information about the client, then it must be able to recover if the client crashes. If information is not saved, then on each transaction the client must transfer sufficient information for the server to function.</p>
  <p class="zh">如果由服务器保持客户端的状态消息，服务器必须在客户端崩溃的时候进行修复。如果服务器没有储存状态消息，那么客户端的每次事务交互都需要提供足够的消息来让服务器进行操作。</p>

  <p class="en">If the connection is unreliable, then additional handling must be in place to ensure that the two do not get out of synch. The classic example is of bank account transactions where the messages get lost. A transaction server may need to be part of the client-server system.</p>
  <p class="zh">如果连接是不可靠的，那么必须要有额外的处理程序来确保双方没有失去同步。一个消息丢失的典型例子就是银行账号交易系统。交易系统是客户端与服务器交互的一部分。</p>

  <h3 id="heading_id_18" class="en">Application State Transition Diagram</h3>
  <h3 id="heading_id_18" class="zh">应用状态转换图</h3>

  <p class="en">A state transition diagram keeps track of the current state of an application and the changes that move it to new states.</p>
  <p class="zh">一个状态转换图清晰说明了当前应用的状态和进入到新的状态需要的转换。</p>

  <p class="en">Example: file transfer with login:</p>
  <p class="zh">例如：带登陆功能的文件传输：</p>

  <div class="sgc-1"><img alt="" src="../Images/app.png" /></div>

  <p class="en">This can also be expressed as a table</p>
  <p class="zh">这个也可以使用一个表来表示</p>

  <div class="sgc-1">
    <table border="1">
      <tr>
        <th>Current state</th>

        <th>Transition</th>

        <th>Next state</th>
      </tr>

      <tr>
        <td rowspan="2">login</td>

        <td>login failed</td>

        <td>login</td>
      </tr>

      <tr>
        <td>login succeeded</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td rowspan="4">file transfer</td>

        <td>dir</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>get</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>logout</td>

        <td>login</td>
      </tr>

      <tr>
        <td>quit</td>

        <td>-</td>
      </tr>
    </table>
  </div>

  <h3 id="heading_id_19" class="en">Client state transition diagrams</h3>
  <h3 id="heading_id_19" class="zh">客户端状态转换图</h3>

  <p class="en">The client state diagram must follow the application diagram. It has more detail though: it <em>writes</em> and then <em>reads</em></p>
  <p class="zh">客户端状态转换图就和应用转换图一样。不同的就是要注意更多细节：它包含有<em>读</em> 和 <em>写</em>操作</p>

  <div class="sgc-1">
    <table border="1">
      <tr>
        <th>Current state</th>

        <th>Write</th>

        <th>Read</th>

        <th>Next state</th>
      </tr>

      <tr>
        <td rowspan="2">login</td>

        <td rowspan="2">LOGIN name password</td>

        <td>FAILED</td>

        <td>login</td>
      </tr>

      <tr>
        <td>SUCCEEDED</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td rowspan="7">file transfer</td>

        <td rowspan="2">CD dir</td>

        <td>SUCCEEDED</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>FAILED</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td rowspan="2">GET filename</td>

        <td>#lines + contents</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>ERROR</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td rowspan="2">DIR</td>

        <td>#files + filenames</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>ERROR</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>quit</td>

        <td>none</td>

        <td>quit</td>
      </tr>

      <tr>
        <td>logout</td>

        <td>none</td>

        <td>login</td>
      </tr>
    </table>
  </div>

  <h3 id="heading_id_20" class="en">Server state transition diagrams</h3>
  <h3 id="heading_id_20" class="zh">服务器状态转换图</h3>

  <p>The server state diagram must also follow the application diagram. It also has more detail: it <em>reads</em> and then <em>writes</em></p>
  <p>服务器状态转换图也和应用转换图一样。不同的就是也要注意更多细节：它包含有 <em>读</em> 和 <em>写</em>操作</p>

  <div class="sgc-1">
    <table border="1">
      <tr>
        <th>Current state</th>

        <th>Read</th>

        <th>Write</th>

        <th>Next state</th>
      </tr>

      <tr>
        <td rowspan="2">login</td>

        <td rowspan="2">LOGIN name password</td>

        <td>FAILED</td>

        <td>login</td>
      </tr>

      <tr>
        <td>SUCCEEDED</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td rowspan="7">file transfer</td>

        <td rowspan="2">CD dir</td>

        <td>SUCCEEDED</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>FAILED</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td rowspan="2">GET filename</td>

        <td>#lines + contents</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>ERROR</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td rowspan="2">DIR</td>

        <td>#files + filenames</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>ERROR</td>

        <td>file transfer</td>
      </tr>

      <tr>
        <td>quit</td>

        <td>none</td>

        <td>quit</td>
      </tr>

      <tr>
        <td>logout</td>

        <td>none</td>

        <td>login</td>
      </tr>
    </table>
  </div>

  <h3 id="heading_id_21" class="en">Server pseudocode</h3>
  <h3 id="heading_id_21" class="zh">服务器伪代码</h3>
  <pre><code>
state = login
while true
    read line
    switch (state)
        case login:
            get NAME from line
            get PASSWORD from line
            if NAME and PASSWORD verified
                write SUCCEEDED
                state = file_transfer
            else
                write FAILED
                state = login
        case file_transfer:
            if line.startsWith CD
                get DIR from line
                if chdir DIR okay
                    write SUCCEEDED
                    state = file_transfer
                else
                    write FAILED
                    state = file_transfer
            ...
</code></pre>

  <p class="en">We don't give the actual code for this server or client since it is pretty straightforward.</p>
  <p class="zh">由于这个伪代码已经足够清晰了，所以我们并不用给出具体的代码了。</p>

  <h2 id="heading_id_22" class="en">Summary</h2>
  <h2 id="heading_id_22" class="zh">总结</h2>

  <p class="en">Building any application requires design decisions before you start writing code. For distributed applications you have a wider range of decisions to make compared to standalone systems. This chapter has considered some of those aspects and demonstrated what the resultant code might look like.</p>
<p class="zh">任何应用程序在开始编写前都需要详尽的设计。开发一个分布式的系统比开发一个独立系统需要更宽广的视野和思维来做决定和思考。这一章已经考虑到了一些这样的问题，并且展示了最终代码的大致样子。</p>
  <!--
<h2>Internet Mail Format</h2>

The Internet Mail Format is a character based format that has been
extended in many ways past its original use. For example, it forms the
underlying representation of the Web.

<h3>Header Format</h3>

A mail message consists of header information followed by the data body.
The header may contain an open-ended amount of information (for mail,
From, To, Subject, Date, Sender, CC, References, ...). This makes it useful
for any other text-based protocol in that by conforming to them you
satisfy a well-known standard. Intermediate systems also know how to 
convert to their own format on the way in, and back to standard format
on the way out.

 <P>
The header consists of an indefinite number of (logical) lines. The
header is terminated by a blank line
<PRE>
From: jan
To: you

// body starts after the blank line
</PRE>

 <P>
The line terminator in Unix is LF, under DOS is CR-LF. The Internet
standard is CR-LF. Special rules exist for s/w that breaks this.
The standard allows long lines to be broken into multiple lines that
still form one logical line: if the CR-LF is immediately followed by
a space or tab, then it is still part of the logical line
<PRE>
some of a 
 logical line
        split over several
   physical lines
</PRE>
Stages may split/reassemble lines for local convenience in the header 
using this rule. Some s/w doesn't understand this mechanism, apparently.



<h3>MIME</h3>

(Multipurpose Internet Mail Extensions).
This is designed for two purposes: firstly to allow messages
composed of multiple parts (e.g. an archive of messages),
and secondly to handle non-ASCII data.

 <P>
Extra fields are added to a message header field:
<PRE>
Content-Type:  &lt;toplevel-type/specific-type&gt;
Content-Transfer-Encoding: &lt;encoding&gt;
</PRE>
The standard toplevel types are application, audio, image,
message, multipart, text, video. All non-standard types 
<I>must</I> begin with x-, e.g. x-compress.
For each toplevel type there is a set of minor types, such as
image/jpeg, image/gif. Non-standard minor types must also
begin with x-, such as image/x-portable-bitmap.

 <P>
The encoding is to tell whether it is sent in e.g.7bit,
case-insensitive, quoted-printable, etc.

 <P>
To decide when a message part is finished, this RFC uses
a terminating string. I have also seen a header
Content-Length in the Web protocol - not sure where that
fits in.

<h3>Date/Time and Money Format</h3>

There are a large number of date/time formats: US month+day, UK day+month,
`:' separated 12:00 or `.' separated 12.00. Months and days may be numeric
or text, in many languages. Years may be two digit or four digit.
RFC 822 has a time format, updated by RFC 1123. Alternatives are in 
RFC 1036 and the C standard function acstime().

 <P>
Money formatting is awful. The currency symbol can appear before, after
or in the middle. Negative amounts can have the minus sign in lots of
places. I don't know a standard.

<h3>Customisation</h3>

Based on a Content-Type, a process may need to perform different actions.
These may involve external programs. For example, a JPEG image would need
the xv program to run under Unix, an MPEG movie would use mpeg_play.
RFC 1524 defines a standard file format that a process can look use to
find the local external program handler. The simplest form is
<PRE>
type; external-program
</PRE>
as in
<PRE>
image/jpeg;             xv %s
application/x-latex;    latex %s
</PRE>


<h2>Debugging</h2>

Telnet is normally used for remote connection to a telnet "daemon", telnetd,
to allow a remote terminal session.  However, by specifying the port number,
telnet can connect to <I>any</I> server with a fixed port number. For
example, 
<PRE>
telnet localhost 13
</PRE>
will result in output such as
<PRE>
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Mon Apr  1 19:44:18 1996
Connection closed by foreign host.
</PRE>
The message from the daytime server "Mon Apr  1 19:44:18 1996" 
appears among the telnet messages. 

 <P>
For daytime, the server sends the time and closes the connection.
An httpd server will wait for a request, send a reply and then close
the connection:
<PRE>
telnet jan.newmarch.name 80
GET http://jan.newmarch.name/
</PRE>
will result in index.html on pandonia being delivered
<PRE>
HTTP/1.0 200 Document follows
MIME-Version: 1.0
Server: CERN/3.0
Date: Monday, 01-Apr-96 09:48:59 GMT
Content-Type: text/html
Content-Length: 4832
Last-Modified: Wednesday, 11-Oct-95 01:30:14 GMT

&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;
...
&lt;/BODY&gt;
Connection closed by foreign host.
</PRE>

 <P>
Ohter procotols will accept a different set of messages, maintain the
connection for longer, etc. With all text-based protocols, telnet gives
a good way of interactive debugging.

 <P>
Non-character protocols are harder to debug. One needs to be able to
analyse the byte stream, which generally requires a separate program.
To manage this, you usually need to insert a "filter" program between 
client and server. This filter will read messages from, say, the client
and forward them to the real server. The client will connect to the
filter on one port while the server is on a different one. Then the filter
can print the messages in readable format while still relaying them.

<h2>Security</h2>

In a connectionless system, authorisation may need to be performed on
every datagram.

 <P>
In a connection-oriented system, generally authorisation will only need
to be performed at connection time. Applications with multiple security
access levels may need finer-grained security access, granted to individual
transactions or to a session of transactions.

 <P>
Security may be done by passwords, private key exchange, public key
messages, or by using a token from a security server. 

<h2>State</h2>

Applications often make use of state information to simplify what is going
on. For example
<UL>
<LI> 
Keeping file pointers to current file location
<LI> 
Keeping current mouse position
<LI> 
Keeping current customer value.
</UL>
In a distributed system, such state information may be kept in the client,
in the server, or in both.

 <P>
The important point is to whether one process is keeping state information
about <I>itself</I> or about the <I>other</I> process. One process may keep
as much state information about itself as it wants, without causing any
problems. If it needs to keep information about the state of the other
process, then problems arise: the process' actual knowledge of the state
of the other may become incorrect. This can be caused by loss of messages
(in UDP), by failure to update, or by s/w errors. 

 <P>
An example is reading a file. In single process applications the file 
handling code runs as part of the application. It maintains a table of
open files and the location in each of them. Each time read or write is done
this file location is updated. In the DCE file system, the file server
keeps track of a client's open files, and where the client's file pointer
is. If a message could get lost (but DCE uses TCP) these could get 
out of synch. If the client crashes, the server must eventually timeout
on the client's file tables and remove them. In NFS, the server does
not maintain this state. The client does.
Each file access from the client that reaches the server must open the 
file at the appropriate point, as given by the client, to perform the
action.

<P>
If the server maintains information about the client, then it must be
able to recover if the client crashes. If information is not saved,
then on each transaction the client must transfer sufficient information
for the server to function. 

 <P>
If the connection is unreliable, then additional handling must be in
place to ensure that the two do not get out of synch. The classic example
is of bank account transactions where the messages get lost.
A transaction server may need to be part of the client-server system.

<h2>Handling Errors</h2>

Errors may occur due to timeouts, lost messages, or data in the
wrong format.
 
<P>



<h2>
The Common Client Interface
</h2>
CCI was a new interface with Mosaic 2.5 for X. 
It is designed to allow control of a Mosaic browser so that it can be
told to fetch documents, and also so that Mosaic can tell an application
what documents it has fetched.
<p>
An example use of this is for a teacher/student environment, where a
teacher is using an instance of Mosaic, and the student instances are all
mirroring the master instance. One possible architecture for this is
where the master is told about the students (somehow) and then sends
information to each of them whenever it does anything. The student
instances in turn will have to listen and obey the master.
This is a case of point to multipoint.

<p>
While it solves the problem, it has a lot of ``special case'' feel to it.
It means that the master must be able to maintain a list of clients
which have to be instances of Mosaic for it to work (why must it be 
limited in this way?). Similarly, the student instances must be listening
to something that must be Mosaic again. How should they find each other
given these types of constraints.

<p>
The requirements also rule out another related use: an instance of Mosaic
displaying documents as told to by a script file, for automated
demonstrations. Should this mechanism also know about files as well
as networks?

<p>
A more general solution is for an instance of Mosaic to be able to tell
an arbitrary application which documents it is fetching, and for an
arbitrary application to be able to tell Mosaic to fetch some
documents. The teacher/student will then be solved by a special-purpose
application sitting between teacher and student instances of Mosaic.

<p>
The protocol is an ASCII based one, compliant with RFC 822. Data
is passed in compliance with the MIME 1.0 specification.
Command lines are terminated with a CR-LF.
The browser (Mosaic) is regarded as the server, any application talking
to it is the client.
The primary components are
<xmp>
GET <url>
SEND ANCHOR [STOP]
DISCONNECT
</xmp>
Each command sent is acknowledged. The acknowledgements are of the form
<xmp>
1xx - informative message
2xx - command ok
3xx - command ok + additional output
4xx - client error
5xx - server error
</xmp>
Whenever the user selects an anchor for Mosaic to display, it will also
send this information to all clients that have sent SEND ANCHOR.
The format of these messages is
<xmp>
301 ANCHOR <url>
</xmp>

<p>
The xwebteach application sends a SEND ANCHOR to the teacher Mosaic.
Each time it receives an anchor by a 301 ANCHOR &lt;url&gt;,
it sends this on to each student Mosaic that is connected by
GET &lt;url&gt;.

<p>
The changes made to Mosaic are quite general.
It needs to maintain a list of clients that it is connected to and
that are active in wanting URL information sent.
Each time a URL is selected, it uses this list to send the anchor.
In addition, it must also receive messages from each port that used
by an application.

<p>
There is no registration part to the protocol. It is assumed that the
user of xwebteach and all servers will set up the common ports and
machines that are used. Connections are set up within the CCI library
and all error checking is done internally to this library.

<p>
The security side of CCI at present is very weak - almost non-existent.
Mosaic by default starts off by not listening on any port. The user
selects by menu which port to listen on. It will only listen on one port
so can be sent messages from only one client. Because the user has choice of
port number and whether listening is enabled, the chances are low that
an intruder will get in. At worst though, it will only be able to
monitor your choice of URL's, or select its own.

<h2>The X Session Manager</h2>
The X Session Manager is a new protocol introduced in X11R6.
It allows one to capture the entire state of your current X Windows
environment, so that it can be restored to the same point later.
The intent is that you can save session state before logging off,
and have it restored when logging on.

<p>
The heart of this protocol is the SaveYourself command sent from the
session manager (SM) to any client (application) that it is managing.
It can also send a Die command to terminate any client, which it will
do when the session is finishing. It must also be able to query the
client for how to start it up again later, for its RestartCommand.

<p>
Clients are expected to locate the server in some unspecified manner.
The sample implementation uses a Unix environment variable set by
the session manager, and then read by each client that is forked by
the session manager.

<p>
When a client finds the session manager it informs it that it wants to
be managed by sending a RegisterClient. The SM replies with a
RegisterClientReply. There are actually two possibilities by the time
we get to this point: the client is starting for the first time, or is
being restarted. How they tell the difference is that the client is
expected to maintain state: it keeps an ID that will be null on first
startup but non-null afterwards.  The server sends this ID as part
of the RegisterClientReply.
This ID is expected to be part of the RestartCommand.

<p>
When a client is started the first time, no state has been saved about
it, so the SM sends it a SaveYourself message straight away.

<p>
When a client receives a SaveYourself message, it is expected to save
state in such a way that it can be restored. Suppose it is an editor,
with an unnamed file in the buffer. It should be able to perform
an interaction with the user to name this file first. Many applications
may want to do this. To organise the order of these requests, the SM
will tell a client to have an interaction, but only if the client has
asked for it.

<p>
Thus there may be an InteractRequest from the client to
SM, and an Interact message from SM to client. Only after the client
sends InteractDone will the SM continue. Finally, a client should
say that its save is complete by SaveYourselfDone.

<p>
This is all complex enough that a state transition diagram is useful on
both client and SM sides. On the client side it is
<xmp>
start:
  ICE protocol setup complete ->
        register

register:
  send RegisterClient ->
        collect-id

collect-id:
  receive RegisterClientReply ->
        idle

idle:
  receive Die -> die
  receive SaveYourself ->
        freeze-interaction

freeze-interaction:
  freeze user interaction ->
        save-yourself

save-yourself:
  send InteractRequest ->
        interact-request
  send SaveYourselfDone ->
        idle

interact-request:
  receive Interact ->
        interact

interact:
  send InteractDone ->
        save-yourself
</xmp>

<p>
There is a byte-level encoding of the protocol, with a fixed range of
data types, such as one-byte, two-byte, four-byte unsigned integers,
and sequences of integers.

<p>
Security is managed by the ICE library (see later). This is able to
implement any security scheme, but the default is the MIT MAGIC-COOKIE
scheme.


<hr>
<h2>Libraries and callbacks</h2>
A protocol may be designed for one-off use, where there is only one
possible client, and only one possible server. Alternatively, it may
be designed as a reusable library.

<p>
Examples of one-off are telnet, ftp. In these cases the protocol is a
quite visible part of the applications, and there will be loops that
poll the connection port as part of the applications.

<p>
For a library, there will need to be some interface between the library
and the application in which it used, so that when certain protocol
events occur appropriate application-specific code can be called.

<p>
In the X Window system all of the network interface is hidden behind
a call XNextEvent(). This returns each time a message is received,
with a data structure filled in with information about the message.
The different data structures all have a first field set to the type
of message. The application sets up a loop around XNextEvent() and
within that loop branches to application code on the type of event.
This is similar to the Microsoft Windows event loop, but of course
that does not use a client-server architecture.
<xmp>
while XNextEvent(&event) 
  switch (event.type) {
     ...
     ...
  }
</xmp>

<p>
An alternative, that goes well with an object-oriented approach, is to
use callback functions. An application registers a function to a message
handler. Each time the message comes in, the library sorts out which
message handler to call internally, and the message handler then calls
each function on its callback list. this reduces the visibility of the
protocol details, and just means that the library interface is reduced
to registering callbacks for each message type.
<xmp>
XtAddCallback(XmNactivateCallback,
              my_callback_func,
              client_data)
</xmp>

<h3>CCI Library</h3>

The CCI library can be used by any application. It is set up
specifically to talk the CCI protocol, and nothing else.

 <P>
Some functions on the client side are
<PRE>
MCCIInitialize(void)
MCCIConnect(serverAddr, port, callback, callbackData)
MCCIPoll(serverPort)
MCCISendAnchor(serverPort, on, callback, callbackData)
MCCIDisconnect(serverPort)
</PRE>

Two of these functions take callback functions as arguments.
The callback to MCCIConnect() is called when either the server
or client disconnects. This can be at some arbitrary time,
and allows the client to clean up properly. 
 <P>
MCCISendAnchor() registers a callback with the library that
is called whenever the server fetches a new anchor and the client
wants to know about it.
The server sends a message 301 ANCHOR &lt;url&gt;.
The CCI library gets this.
When MCCIPoll() executes, it sees that there is a message
and calls the callback function which must be of type
<PRE>
void callback(char *anchor, void *callbackData)
</PRE>

 <P>
The server side of the library is not public, since it was
only meant to be used by Mosaic. It is pretty grotty, 
heavily tied up with Mosaic GUI code, etc.


<h3>The ICE library</h3>
The socket interface is standardised enough so that the real work -
designing and implementing the protocol for any system - should be
all that needs to be done. The ICE (Inter Client Exchange) library
is an X Consortium standard devised for this. This handles the common
matters of authentication, version negotiation, data typing and
connection management.
It also allows an <I>arbitrary</I> protocol to be setup 
above TCP/IP.

<p>
ICE gives connection setup, consisting of byte order exchange, 
authentication and connection information exchange. Then protocol
negotiation is done. This agrees on the protocol used, and may also
include authentication (multiple protocols may share a connection).
This also registers a callback function that is used to handle any
of the messages of that protocol. When a message arrives this
callback function, belonging to the application, is called to process
the message. There are a variety of functions to read message data
of various sizes.

<hr>
<h2>Overheads</h2>
For two processes to communicate over the wire, a message has to be
constructed, sent, and recognised at the other end. This overhead may
be quite substantial. It is proposed that this explains why an X Window
application has a long startup time compared to a Microsoft Windows
application. It is suggested that X Windows can be made much quicker on local
systems by mapping the X server into an application's address space and
making local procedure calls.



<h2>Ftp protocol</h2>
The client needs to be able to ask for a directory listing. 
The server needs to respond to this with the listing of the current
directory. (More complex: specify the directory as well.)
So a client to server message is <i> list dir </i>, with response
being a list of files.

<p>
The client needs to be able to ask for a directory change, to a directory
that it specifies. If the client asks the server to something based on
server state, then the request may be honoured or fail. The client
needs to be told of this. So this needs a reply, of success or fail.

<p>
The purpose of this CS setup is to transfer files. So the client needs
to be able to send a "get" message. The file may or may not exist,
and may have restrictive permissions. So there is a question of whether
or not the transfer will go ahead, and if ok, how it takes place.

<p> 
It may be convenient for the client to be able to determine which
is the current directory on the server.

<h2>Synchronisity</h2>
Should the client fire off messages and deal with replies as they come
in, or send a message and wait for a reply before continuing?
Several messages need replies, and it is not likely that they will take
too long in coming, so a synchronous rather than asynchronous model
would be more appropriate. This is also much simpler.

<h2>State</h2>
Each time a client wants to talk to a server, it could establish a
per-message connection that is terminated after each reply.
Alternatively, it could hold a connection open for the duration of
a session.

<p>
In the first mechanism, any state would have to be maintained by the
client, and sent anew on establising the connection. In the second,
the server could maintain state that lasts for any one session.
Is there any state info required? Yes - the current directory on the
server.

<p>
If connections are made and dissolved on a per-message basis, then 
multiple clients could talk "simultaneously" to a single server.
If a client holds the connection for a session, then other clients
cannot use that port until the session is over. There is no queuing
mechanism. If this is a problem, then each session should be done
using a slave server.

<h2>Encoding</h2>
Should the messages be sent in as compact a form as possible, or in as
convenient a form as possible? There are at least two message types,
"dir" and "get". A compact encoding would be to as separate byte values.
Both sides would have to agree on these values. A simple encoding
would be as strings (terminated in some way). Again there would have to be
agreement on both sides, but less possibility of error.

<h2>Protocol</h2>
<pre>
client                     server
======                     ======

dir
                           send list of files
                           terminate with blank
                           line

cd &lt;dir&gt;
                           change dir
                           send blank if failed
                           send newdir if succeed

get &lt;file&gt;
                           send file
</pre>

Here is the client program

<PRE>
/* TCP client that finds the
   time from a server */

#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;sys/param.h&gt;

#define SIZE 1024
char buf[SIZE];

#define TIME_PORT 2013

#define MAX 1024

char *GetLine(int fd)
{
  static char line[MAX];
  static char netread[MAX] = "";
  int n, len;
  char *p;

  len = strlen(netread);

  /* look for \r\n in netread buffer */
  p = strstr(netread, "\r\n");
  if (p == NULL) {
    /* fill buff - no \r\n found */
    n = read(fd, netread+len, MAX-len);
    len += n;
    netread[len] = '\0';
    p = strstr(netread, "\r\n");
    if (p == NULL) {
      /* still cant find line terminator,
         discard this one and try again
       */
      netread[0] = '\0';
      return GetLine(fd);
    }
  }
  *p = '\0';
  strcpy(line, netread);
  
  /* copy rest of buf down */
  memmove(netread, p+2, strlen(p+2)+1);

  return line;
}

void SendDir(int fd)
{
  int n;
  char *s;

  write(fd, "dir\r\n", 5);
  /* read response */
  while (1) {
    s = GetLine(fd);
    if (*s == '\0')
      break;
    printf("%s\n", s);
  }
}

void ChangeDir(char *dir, int fd)
{
  int n;

  strcat(dir, "\r\n");
  write(fd, dir, strlen(dir));
  /* read response */
  n = read(fd, buf, 1024);
  write(1, buf, n);
}

void GetFile(char *file, int fd)
{
  int n;

  strcat(file, "\r\n");
  write(fd, file, strlen(file));
  /* read response */
  n = read(fd, buf, 1024);
  write(1, buf, n);
}

void HandleMsgs(int fd)
{
  char buff[MAXPATHLEN + 4];

  while (gets(buff) != NULL) {
    if (strncmp(buff, "quit", 4) == 0) {
      exit(0);
    }

    if (strncmp(buff, "dir", 3) == 0) {
      SendDir(fd);
    } else

    if (strncmp(buff, "cd ", 3) == 0) {
      ChangeDir(buff, fd);
    } else

    if (strncmp(buff, "get ", 4) == 0) {
      GetFile(buff, fd);
    } else {

      fprintf(stderr, "illegal command\n");
    }
  }
}


int main(int argc, 
         char *argv[])
{
  int sockfd;
  int nread;
  struct sockaddr_in serv_addr;

  if (argc != 2) {
    fprintf(stderr,
        "usage: %s IPaddr\n",
        argv[0]);
    exit(1);
  }


  if ((sockfd =
       socket(AF_INET,
              SOCK_STREAM, 0))
      < 0) {
    perror(NULL);
    exit(2);
  }


  serv_addr.sin_family =
            AF_INET;
  serv_addr.sin_addr.s_addr =
            inet_addr(argv[1]);
  serv_addr.sin_port =
            htons(TIME_PORT);
  if (connect(sockfd,
             &serv_addr,
             sizeof(serv_addr))
      < 0) {
    perror(NULL);
    exit(3);
  }

  HandleMsgs(sockfd);
  close(sockfd);
  exit(0);
}

</PRE>


Here is the server

<PRE>
#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;ctype.h&gt;
#include &lt;sys/dir.h&gt;
#include &lt;limits.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/param.h&gt;

#define SIZE 1024
char buf[SIZE];

#define TIME_PORT 2013

#define MAX 1024

char *GetLine(int fd)
{
  static char line[MAX];
  static char netread[MAX] = "";
  int n, len;
  char *p;

  len = strlen(netread);

  /* look for \r\n in netread buffer */
  p = strstr(netread, "\r\n");
  if (p == NULL) {
    /* fill buff - no \r\n found */
    n = read(fd, netread+len, MAX-len);
    len += n;
    netread[len] = '\0';
    p = strstr(netread, "\r\n");
    if (p == NULL) {
      /* still cant find line terminator,
         discard this one and try again
       */
      netread[0] = '\0';
      return GetLine(fd);
    }
  }
  *p = '\0';
  strcpy(line, netread);
  
  /* copy rest of buf down */
  memmove(netread, p+2, strlen(p+2)+1);

  return line;
}

void SendDir(int client_fd)
{
  DIR *dirp;
  struct direct *dp;
  char buff[MAXPATHLEN + 3];

  dirp = opendir(".");
  for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
    strcpy(buff, dp->d_name);
    strcat(buff, "\r\n");
    write(client_fd, buff, strlen(buff));
  }
  strcpy(buff, "\r\n");
  write(client_fd, buff, 2);
}

void ChangeDir(char *dir, int client_fd)
{
  char buff[MAXPATHLEN + 3] = "";

  /* remove leading blanks */
  while (isspace(*dir))
    dir++;

  if (chdir(dir) == 0)
    /* success */
    getcwd(buff, MAXPATHLEN+1);

  strcat(buff, "\r\n");
  write(client_fd, buff, strlen(buff));
}

void SendFile(char *filename, int client_fd)
{
  /* remove leading blanks */
  while (isspace(*filename))
    filename++;
}

void HandleMsgs(int sockfd, int client_sockfd)
{
  char *s;

  s = GetLine(client_sockfd);
  if (s == NULL)
    return;

  if (strncmp(s, "dir", 3) == 0) {
    SendDir(client_sockfd);
  } else

  if (strncmp(s, "cd ", 3) == 0) {
    ChangeDir(s+3, client_sockfd);
  } else

  if (strncmp(s, "get ", 4) == 0) {
    SendFile(s+4, client_sockfd);
  } else {

    /* error stub */
  }
}

int main(int argc, 
         char *argv[])
{
  int sockfd, client_sockfd;
  int nread, len;
  struct sockaddr_in serv_addr,
                   client_addr;
  time_t t;

  if ((sockfd =
       socket(AF_INET,
              SOCK_STREAM, 0))
      < 0) {
    perror(NULL);
    exit(2);
  }
  serv_addr.sin_family =
            AF_INET;
  serv_addr.sin_addr.s_addr =
         htonl(INADDR_ANY);
  serv_addr.sin_port =
            htons(TIME_PORT);

  if (bind(sockfd, &serv_addr, sizeof(serv_addr)) < 0) {
    perror(NULL);
    exit(3);
  }

  listen(sockfd, 5);
  for (;;) {
    len = sizeof(client_addr);
    client_sockfd =
          accept(sockfd,
                 &client_addr,
                 &len);
    if (client_sockfd == -1) {
      perror(NULL);
      continue;
    }
    for (;;)
      HandleMsgs(sockfd, client_sockfd);
    close(client_sockfd);
  }
}

</PRE>


<h2>References</h2>

Unicode Consortium <I>The Unicode Standard</I> ISBN 0-201-56788-1, 
QA 268.U55

 <P>
D. H. Crocker <I>Standard for the Format of ARPA Internet Text Messages</I>
IETF RFC 822

<P>
N. Borenstein <I>A User Agent Configuration Mechanism for Multimedia
Mail Format Information</I>
IETF RFC 1524

<P>
N. Borenstein and N. Freed <I>MIME Part One: Mechanisms for Specifying
and Describing the Format of Internet Message Bodies</I>
IETF RFC 1521
 
<P>
CCI specification http://www.ncsa.uiuc.edu/SDG/Software/XMosaic/CCI/cci-spec.html
 <P>
The IETF RFC's may be obtained from 
ftp://ietf.org/internet-drafts/
or http://www.garlic.com/~lynn/rfcietf.htm
-->

  <p>Copyright Jan Newmarch, jan@newmarch.name</p>

  <p>If you like this book, please contribute using Flattr <a class="FlattrButton sgc-10" href="http://jan.newmarch.name/go/index.html"></a><br />
  or donate using PayPal</p>

  <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
    <input name="cmd" type="hidden" value="_s-xclick" /> <input name="encrypted" type="hidden" value="-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCCw7fVj6fuHxYMvE0PBlURcRgBFb1s4TxTUDgsS6BgkdJPt2GF8NFPNvE/oFvPNY2jBGrXSIkxCr9dFYzraKC8csPASWb0z9l8swwbIHWgrvb5cuaVuLbtRzesh94sqyh9MmZ5U1xcMrMtlw1S60gK5lPbKPsXzcY74brjt44J7jELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIAXtre9K+AiWAgYiJVN0CmxAPscp0u0O8R0mD+cNz/Fe3lNIrqqMPplkri20WbbVxhbRwJTjtOxcLMbmSIeC8oWh14aSy9Jptgm1wNlQYADQQUgMnR/qIlYgHmXjJ4C6wZteqNVJn+RKfM/tS008Ola5SJABaGe9BmRSQCjMKqEyqm3Mx2hoLeWMXeyoMaW3Xteg6oIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTEwNTAyMDcwNzQ1WjAjBgkqhkiG9w0BCQQxFgQUgvHyq74JT8DnmViqEqG5KpIW0cAwDQYJKoZIhvcNAQEBBQAEgYAzycmlaZMZjkmYniVBUVTQeywigBo+80toDP2g9+yCzO4mG1Abmfcr/S1XdT8djFA9w37F+F+nSkP857evscUhns30c9wYuPoiNudkJMOkYegqyq+EI4AMNGPuQNZ+4vznmqTgFTn9iQjONC8NGQ/0GuCCQ/AqJZs/0ZiWivlPhA==-----END PKCS7----- " /> <input alt="PayPal - The safer, easier way to pay online." border="0" name="submit" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/en_AU/i/btn/btn_donateCC_LG.gif" type="image" /> <img alt="" border="0" height="1" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/en_AU/i/scr/pixel.gif" width="1" />
  </form>
</body>
</html>
